﻿using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Linq;
using System.Diagnostics.Contracts;
using OpenWaves.ActivityFeed.Web.Modules;
using Raven.Client.Linq;
using OpenWaves.ActivityFeed;

namespace OpenWaves.ActivityFeed.Web
{
    public abstract class UpdatesBuilder
    {
        private readonly IUpdateDtoBuilder updateDtoBuilder;

        public PagerInfo PagerInfo { get; set; }
        public string SearchTerm { get; set; }
        public IEnumerable<UserGroup> UserGroups { get; set; }
        public IEnumerable<Topic> TopicsFilter { get; set; }

        protected UpdatesBuilder(IUpdateDtoBuilder updateDtoBuilder)
        {
            Contract.Requires<ArgumentNullException>(updateDtoBuilder != null);
            this.updateDtoBuilder = updateDtoBuilder;
        }

        [SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")]
        protected abstract IQueryable<Update> GetUpdatesInternal(string searchTerms);

        [SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")]
        [SuppressMessage("Microsoft.Design", "CA1021:Consider a design that does not require that 'paginationStatus' be an out parameter.")]
        public IEnumerable<UpdateDto> GetUpdates(out PaginationStatus paginationStatus)
        {
            Contract.Requires<NullReferenceException>(this.UserGroups != null);
            Contract.Requires<NullReferenceException>(this.TopicsFilter != null);

            var updates = this.GetUpdatesInternal(this.SearchTerm).SecurityTrim(this.UserGroups).FilterByTopics(this.TopicsFilter);

            // get 1 more update to check paginationStatus
            var updatesList = updates.GetPage(this.PagerInfo.DateFrom, this.PagerInfo.PageSize + 1).ToList();

            if ( updatesList.Count > this.PagerInfo.PageSize)
            {
                updatesList = updatesList.Take(updatesList.Count -1).ToList();
                paginationStatus = PaginationStatus.PartialResults;
            }
            else
            {
                paginationStatus = PaginationStatus.CompleteResults;
            }
            return updatesList.Select(u => updateDtoBuilder.ConvertUpdateToUpdateDto(u));
        }

        public static UpdatesBuilder ForPersonal(string subscriberId, IActivityFeedService service, IUpdateDtoBuilder updateDtoBuilder)
        {
            return new UpdatesForPersonal(subscriberId, service, updateDtoBuilder);
        }

        public static UpdatesBuilder ForGlobal(IActivityFeedService service, IUpdateDtoBuilder updateDtoBuilder)
        {
            return new UpdatesForGlobal(service, updateDtoBuilder);
        }
    }

    public class UpdatesForPersonal : UpdatesBuilder
    {
        private readonly IActivityFeedService service;
        private readonly string subscriberId;

        public UpdatesForPersonal(string subscriberId, IActivityFeedService service, IUpdateDtoBuilder updateDtoBuilder)
            : base(updateDtoBuilder)
        {
            Contract.Requires<ArgumentNullException>(service != null);
            this.service = service;
            this.subscriberId = subscriberId;
        }

        protected override IQueryable<Update> GetUpdatesInternal(string searchTerms)
        {
            var subscriber = this.service.GetSubscriber(this.subscriberId);
            return this.service.GetUpdates(searchTerms).ForSubscriber(subscriber);
        }
    }

    public class UpdatesForGlobal : UpdatesBuilder
    {
        private readonly IActivityFeedService service;

        public UpdatesForGlobal(IActivityFeedService service, IUpdateDtoBuilder updateDtoBuilder)
            : base(updateDtoBuilder)
        {
            Contract.Requires<ArgumentNullException>(service != null);
            this.service = service;
        }

        protected override IQueryable<Update> GetUpdatesInternal(string searchTerms)
        {
            return this.service.GetUpdates(searchTerms);
        }
    }


    public class PagerInfo
    {
        public const int DefaultPageSize = 10;

        public DateTime? DateFrom { get; set; }
        public int PageSize { get; set; }

        public static PagerInfo FromQuery(UrlQueryCollection parameters)
        {
            DateTime? dateFromFilter = null;
            DateTime dateFrom;
            if (parameters.ContainsKey("dateFrom") && DateTime.TryParse(parameters["dateFrom"], CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal, out dateFrom))
            {
                dateFromFilter = dateFrom.AddSeconds(-1);
            }
            int? pageSize = null;
            int pageSizeParam;
            if (parameters.ContainsKey("pageSize") && int.TryParse(parameters["pageSize"], out pageSizeParam))
            {
                pageSize = pageSizeParam;
            }
            else
            {
                pageSize = DefaultPageSize;
            }

            return new PagerInfo() { DateFrom = dateFromFilter, PageSize = pageSize.Value };
        }
    }
}
